/*日志器模块的实现:
    支持两种不同的落地模式，同步和异步
        同步：直接利用落地模块完成落地
        异步：先将日志放入到缓冲区中然后再由其他线程进行实际的落地操作
    异步思想:
        异步线程对缓冲区的内容进行处理。如果缓冲区中没有数据了就交换缓冲区，交换的条件是另一个缓冲区也不为空
        为了实现高内聚低耦合的设计思想，异步日志器对缓冲区中数据的处理(比如输出到哪个文件等..)由回调函数来进行
*/
#pragma once
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "util.hpp"
#include "sink.hpp"
#include "level.hpp"
#include "format.hpp"
#include "message.hpp"
#include "looper.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <cstdio>
#include <iostream>
#include <thread>
#include <unordered_map>

namespace wu {
    //日志器基类
    class Logger {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(std::string &logger_name, Formatter::ptr &formatter, LogLevel::value level, 
                    std::vector<LogSink::ptr> &sinks)
            :_logger_name(logger_name), _formatter(formatter), _limit_level(level), _sinks(sinks.begin(), sinks.end())
        {}
        const std::string& name(){return _logger_name; }
        void debug(const std::string &file, size_t line, const std::string &fmt, ...) {
            //1.判断当前的日志等级是否达到了默认输出等级
            if(LogLevel::value::DEBUG < _limit_level) return;
            //2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list va;
            va_start(va, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), va);
            if(ret < 0)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            va_end(va);//va置空
            serialize(LogLevel::value::DEBUG, file, line, res);
            free(res);//释放空间
        }
        void info(const std::string &file, size_t line, const std::string &fmt, ...) {
            if(LogLevel::value::INFO < _limit_level) return;
            //2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list va;
            va_start(va, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), va);
            if(ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            va_end(va);//va置空
            serialize(LogLevel::value::INFO, file, line, res);
            free(res);//释放空间
        }
        void warn(const std::string &file, size_t line, const std::string &fmt, ...) {
            //1.判断当前的日志等级是否达到了默认输出等级
            if(LogLevel::value::WARN < _limit_level) return;
            //2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list va;
            va_start(va, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), va);
            if(ret == -1)
            {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            va_end(va);//va置空
            serialize(LogLevel::value::WARN, file, line, res);
            free(res);//释放空间
        }
        void error(const std::string &file, size_t line, const std::string &fmt, ...) {
            //1.判断当前的日志等级是否达到了默认输出等级
            if(LogLevel::value::ERROR < _limit_level) return;
            //2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list va;
            va_start(va, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), va);
            if(ret == -1) {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            va_end(va);//va置空
            serialize(LogLevel::value::ERROR, file, line, res);
            free(res);//释放空间
        }
        void fatal(const std::string &file, size_t line, const std::string &fmt, ...) {
            //1.判断当前的日志等级是否达到了默认输出等级
            if(LogLevel::value::FATAL < _limit_level) return;
            //2.对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list va;
            va_start(va, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), va);
            if(ret == -1) {
                std::cout << "vasprintf failed" << std::endl;
                return;
            }
            va_end(va);//va置空
            serialize(LogLevel::value::FATAL, file, line, res);
            free(res);//释放空间
        }
    protected:
        void serialize(LogLevel::value level, const std::string &file, size_t line, std::string str) {
            //3.构建LogMsg对象
            LogMsg msg(level, line, file, _logger_name, str);
            //4.通过LogMsg对象得到日志格式化消息字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            //5.日志落地
            log(ss.str().c_str(), ss.str().size());
        }

        virtual void log(const char* data, size_t len) = 0;
    protected:
        std::mutex _mutex;
        std::string _logger_name;//日志器名称
        Formatter::ptr _formatter;
        std::atomic<LogLevel::value> _limit_level;//日志默认等级,只有大于等默认等级的日志才能进行输出
        //设置为原子的是为了提高性能，使用加锁方案也可以，不过频繁的申请和释放锁会造成效率的下降,也可能会出现一些竞争锁的场景
        std::vector<LogSink::ptr> _sinks;//日志的落地方向可以有多个，由用户控制
    };
    
    class AsyncLogger : public Logger {
    public:
        AsyncLogger(std::string &logger_name, Formatter::ptr &formatter, LogLevel::value level, 
                    std::vector<LogSink::ptr> &sinks, AsyncType looper_type)//根据异步日志消息处理器的安全类型选择哪一种模式
        :Logger(logger_name, formatter, level, sinks)
        //因为realLog函数是类成员函数有个this指针，和需要的回调函数的参数个数不一致，所有需要使用bind重新调整一下参数的个数
        ,_looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type))
        {}

        //将数据写入到生产者缓冲区中
        void log(const char* data, size_t len) override {
            _looper->push(data, len);//looper已经保证了该操作是线程安全的,所以这里就不需要再进行加锁保护了
        }
        //实际的落地函数(将缓冲区的数据进行落地) 
        void realLog(Buffer &buf) {
            if(_sinks.empty()) return;
            for(auto &sink : _sinks) {
                sink->log(buf.begin(), buf.readOfSize());
            }
            //异步日志消息处理器只有一个消费者缓冲区处理线程，因此这里不需要加锁
        }
    private:
        AsyncLooper::ptr _looper;
    };

    class LoggerBuilder {   
    public:
        LoggerBuilder()
            :_limit_level(LogLevel::value::DEBUG)
            ,_looper_type(AsyncType::ASYNC_SAFE)//默认安全的
        {}
        void buildEnableUnSafeAsync() { _looper_type = AsyncType::ASYNC_UNSAFE; };//将异步日志器设置为不安全模式
        void buildLoggerName(const std::string &name) { _logger_name = name; }
        void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }
        void buildFormatter(const std::string &pattern = "") { _formatter = std::make_shared<Formatter>(pattern); }
        template<class SinkType, class ...Args>
        void buildSink(Args &&...args) {
            LogSink::ptr sinks = FactorySink::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(sinks);
        }
        virtual Logger::ptr build() = 0;
    protected:
        AsyncType _looper_type;
        std::string _logger_name;
        LogLevel::value _limit_level;//日志器的等级默认是最低等级BEGUD
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    //2.派生出具体的建造者类1.局部日志器建造者2.全局日志器建造者(后边会添加全局单例日志器管理，将日志器添加到全局管理)
    class LocalLoggerBuilder : public LoggerBuilder {
    public:
        Logger::ptr build() override {
            assert(!_logger_name.empty()); //日志器名称是必须的
            if(_formatter.get() == nullptr) //如果用户没有构建formatter，就用默认的格式化去实例化出一个formatter
                _formatter = std::make_shared<Formatter>();
            if(_sinks.empty()) //如果没有指定落地的方式，就给个标准输出当作默认的落地方式
                _sinks.push_back(std::make_shared<StdoutSink>());
            // 构建日志器
            return std::make_shared<AsyncLogger>(_logger_name, _formatter, _limit_level, _sinks, _looper_type);
        }
    };

    //日志器管理单例类（饿汉模式）
    class LoggerManager {
    public:
        static LoggerManager& getInstance() {
            //C++11之后，局部的静态变量就是线程的安全的
            static LoggerManager eton;
            return eton;
        }
        void addLogger(Logger::ptr &logger) {
            //先判断是否已经存在了该日志器，如果存在不能重复添加
            if(hasLogger(logger->name())) 
                return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        bool hasLogger(const std::string &name) {
            std::unique_lock<std::mutex> lock(_mutex);
            auto pos = _loggers.find(name);
            if(pos == _loggers.end()) 
                return false;
            return true;
        }
        Logger::ptr getLogger(const std::string &name) {
            std::unique_lock<std::mutex> lock(_mutex);
            //存在就返回指定的日志器，不存在就返回空
            auto it = _loggers.find(name);
            if(it == _loggers.end())
                return nullptr;
            return it->second;
        }
        Logger::ptr rootLogger() {
            return _root_logger;
        }
    private:
        LoggerManager() {
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            builder->buildLoggerName("default");
            _root_logger = builder->build();
            _loggers.insert(std::make_pair("default", _root_logger));
        }
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;//默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers;
    }; 
    
    class GlobalLoggerBuilder : public LoggerBuilder {
    public:
        Logger::ptr build() override {
            assert(!_logger_name.empty());//日志器名称是必须的
            if(_formatter.get() == nullptr)
                _formatter = std::make_shared<Formatter>();
            if(_sinks.empty())
                _sinks.push_back(std::make_shared<StdoutSink>());
            // 构建全局日志器
            Logger::ptr logger;
            logger = std::make_shared<AsyncLogger>(_logger_name, _formatter, _limit_level, _sinks, _looper_type);
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}
